![タッチしたオブジェクトを指で動かしました。](https://devio2023-media.developers.io/wp-content/uploads/2011/11/Android.png)
タッチしたオブジェクトを指で動かしました。
以下の動きを目標にしました。
- 指にViewコンポーネント(画像)がついてくる
- 画面の右下のゴミ箱に画像をドラッグすると消える
- 一度ゴミ箱にドラッグした後、ゴミ箱をクリックしたら画面左上に画像を表示する
main.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/FrameLayout01" android:layout_height="match_parent" android:layout_width="match_parent" android:layout_weight="10" android:background="@color/white"> <Button android:layout_gravity="bottom|right" android:layout_width="30dp" android:layout_height="30dp" android:id="@+id/trash" android:background="@drawable/custom_button" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/move" android:id="@+id/ImageView01" /> </FrameLayout>
MainActivity.java
package com.sample.movetest; import android.app.Activity; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnTouchListener; import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageView; public class MainActivity extends Activity implements OnTouchListener, OnClickListener { private FrameLayout frameLayout01; private ImageView target; private Button trash; private int targetLocalX; private int targetLocalY; private int screenX; private int screenY; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); frameLayout01 = (FrameLayout)findViewById(R.id.FrameLayout01); target = (ImageView)findViewById(R.id.ImageView01); target.setOnTouchListener(this); trash = (Button)findViewById(R.id.trash); trash.setOnClickListener(this); } @Override public boolean onTouch(View v, MotionEvent event) { int x = (int)event.getRawX(); int y = (int)event.getRawY(); switch(event.getAction()) { case MotionEvent.ACTION_DOWN: targetLocalX = target.getLeft(); targetLocalY = target.getTop(); screenX = x; screenY = y; break; case MotionEvent.ACTION_MOVE: int diffX = screenX - x; int diffY = screenY - y; targetLocalX -= diffX; targetLocalY -= diffY; target.layout(targetLocalX, targetLocalY, targetLocalX + target.getWidth(), targetLocalY + target.getHeight()); screenX = x; screenY = y; break; case MotionEvent.ACTION_UP: int trashLeft = trash.getLeft() + trash.getWidth()/2; int trashTop = trash.getTop() + trash.getHeight()/2; int targetRight = target.getLeft() + target.getWidth(); int targetBottom = target.getTop() + target.getHeight(); if (targetRight > trashLeft && targetBottom > trashTop) { frameLayout01.removeView(target); } break; } return true; } @Override public void onClick(View v) { int childCount = frameLayout01.getChildCount(); if(childCount == 1) { frameLayout01.addView(target); } } }
■使用するレイアウト
ゴミ箱に画像をドラッグする(重ねる)ので 使用するレイアウトは、FrameLayoutを使いました。 FrameLayoutは、複数のViewを重ねて表示することができます。 位置を何も指定しない場合は、左上に表示されます。 また、後から配置したViewが前面に配置されます。 FrameLayoutの他にRelativeLayoutもViewを重ねて表示することが できます。
■動かす対象Viewのタッチイベントを検知して処理する
MainActivity.javaの14行目
implements OnTouchListener, OnClickListener {
オブジェクトごとのタッチイベントの検知は、 OnTouchListenerインターフェースを実装します。
MainActivity.javaの33行目
target.setOnTouchListener(this);
動かす対象Viewにタッチイベントのリスナー登録をします。
MainActivity.javaの40~87行目
@Override public boolean onTouch(View v, MotionEvent event) { … }
onTouchというコールバックメソッドをオーバーライドします。 Viewを指で動かした時のタッチイベントを検知したいので今回はonTouchを使います。 また、ViewごとでなくActivity全体のタッチイベントの検知は、 onTouchEvent()コールバックメソッドをオーバーライドします。
MainActivity.javaの42~43行目
int x = (int)event.getRawX(); int y = (int)event.getRawY();
タッチした場所の絶対座標を取得します。 絶対座標を取得するにはgetRawX(),getRawY()を使用しますが、 相対座標を取得する場合はgetX(),getY()を使用します。
MainActivity.javaの45~85行目
switch(event.getAction()) { case MotionEvent.ACTION_DOWN: … }
case MotionEvent.ACTION_DOWN: → 押したとき、 case MotionEvent.ACTION_MOVE: → 動かしたとき、 case MotionEvent.ACTION_UP: → 離したときと タッチイベントのアクションごとに処理をわけます。 アクションはDOWN→MOVE→UPの順番で発生します。 今回は使用していないですが、他には ACTION_CANCEL(タッチがキャンセルされたとき) ACTION_OUTSIDE(対象のViewの範囲外をタッチしたとき)があります。
MainActivity.javaの46~54行目
case MotionEvent.ACTION_DOWN: targetLocalX = target.getLeft(); targetLocalY = target.getTop(); screenX = x; screenY = y; break;
押したときの処理
対象Viewの左端座標を親Viewの相対座標として取得し、targetLocalXに入れます。 対象Viewの上端座標を親Viewの相対座標として取得し、targetLocalYに入れます。 タッチした場所のx座標をscreenXに入れます。 タッチした場所のy座標をscreenXに入れます。
MainActivity.javaの56~72行目
case MotionEvent.ACTION_MOVE: int diffX = screenX - x; int diffY = screenY - y; targetLocalX -= diffX; targetLocalY -= diffY; target.layout(targetLocalX, targetLocalY, targetLocalX + target.getWidth(), targetLocalY + target.getHeight()); screenX = x; screenY = y; break;
動かしたときの処理
押したときの座標と動かしたときの座標の差分をdiffX,diffYに入れます。 targetLocalX,targetLocalYに差分を反映させます。 差分を反映させた位置に対象Viewを移動させます。 タッチした場所のx座標をscreenXに入れます。 タッチした場所のy座標をscreenXに入れます。
MainActivity.javaの74~85行目
case MotionEvent.ACTION_UP: int trashLeft = trash.getLeft() + trash.getWidth()/2; int trashTop = trash.getTop() + trash.getHeight()/2; int targetRight = target.getLeft() + target.getWidth(); int targetBottom = target.getTop() + target.getHeight(); if (targetRight > trashLeft && targetBottom > trashTop) { frameLayout01.removeView(target); } break;
離したときの処理
ゴミ箱に入れたと判定する為の位置座標をtrashLeft,trashTopに入れます。 対象の右端座標、下端座標がtrashLeft,trashTop超えた場合は 対象Viewを削除します。
■ゴミ箱をクリックしたときの処理
MainActivity.javaの14行目
implements OnTouchListener, OnClickListener {
クリックイベントの検知は、OnClickListenerインターフェースを実装します。
MainActivity.javaの36行目
trash.setOnClickListener(this);
ゴミ箱用のコンポーネントににクリックイベントのリスナー登録をします。
MainActivity.javaの92~94行目
if(childView == null) { frameLayout01.addView(target); }
動かす対象Viewが画面にない場合は、表示させます。